from selenium import webdriver
from typing import Set, List
import time
import requests
import os
import logging
import json

# Create a logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)
rq = time.strftime('%Y%m%d%H%M', time.localtime(time.time()))
os.makedirs('logs', exist_ok=True)
log_path = 'logs/'
log_name = log_path + rq + '.log'
fh = logging.FileHandler(log_name, mode='w')
formatter = logging.Formatter("%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s")
fh.setFormatter(formatter)
logger.addHandler(fh)


class WebModel:
    def __init__(self, title: str, url: str, xpath: List[str]):
        self.title: str = title
        self.url: str = url
        self.xpath: List[str] = xpath
        
        self.last_status: Set[str] = set()

class UserModel:
    def __init__(self, username: str, token: str):
        self.username: str = username
        self.token: str = token


class ChromeDriver:
    def __init__(self, web_list: List[WebModel], token_list: List[UserModel], stop_browser=False):
        # Web List
        self.web_list: List[WebModel] = web_list
        self.token_list: List[UserModel] = token_list
        self.stop_browser: bool = stop_browser

        # Start Chrome
        if not stop_browser:
            self.__init__browser()

    def __init__browser(self):
        print('Starting browser...')
        logging.info('Starting browser...')
        while True:
            try:
                # Start Chrome
                os.environ["DBUS_SESSION_BUS_ADDRESS"] = '/dev/null'
                chrome_options = webdriver.ChromeOptions()
                chrome_options.add_argument('--no-sandbox')
                chrome_options.add_argument('--disable-dev-shm-usage')
                chrome_options.add_argument('--headless')
                self.browser = webdriver.Chrome(options=chrome_options)
                self.browser.set_page_load_timeout(5)
                self.browser.set_script_timeout(5)
                break
            except Exception as err:
                print(err)
                logging.error(err)
                time.sleep(10)
        print('Finished')
        logger.info('Finished')

    def __close_browser(self):
        print('Closing browser...')
        logger.info('Closing browser...')
        try:
            self.browser.quit()
        except Exception as err:
            print(err)
            logger.error(err)
        print('Finished.')
        logger.info('Finished.')

    def __scan(self, web: WebModel):
        # Get Website
        self.browser.get(web.url)

        # Show log
        print(f'---------------------------------->  Scanning {web.title}')
        logger.info(f'---------------------------------->  Scanning {web.title}')
        
        # Get elements
        new_last_status: Set[str] = web.last_status.copy()
        new_items: List[str] = []

        for i in range(len(web.xpath)):
            text = self.browser.find_element_by_xpath(web.xpath[i]).text
            if text not in web.last_status: # and web.last_status[i] is not None:
                new_items.append(text)

            new_last_status.add(text)

            print(f'Item {i}: {text}')
            logger.info(f'Item {i}: {text}')

        # Set Last Status
        web.last_status = new_last_status

        # Check if there is new item
        print(f'Found {len(new_items)} new items.')
        logger.info(f'Found {len(new_items)} new items.')
        if len(new_items) == 0:
            return

        # Send messages
        for user in self.token_list:
            try:
                print(f'Send new message to {user.username}...')
                logger.info(f'Send new message to {user.username}...')

                message_payload = {
                    'msgtype': 'text',
                    'text': {
                        'content': f'来自{web.title}的网站消息：{";".join(new_items)}，请您查看{web.url}'
                    },
                    'at': {
                        'isAtAll': True
                    }
                }
                header = {'Content-Type': 'application/json'}

                result = requests.post(
                    url=user.token,
                    json=message_payload,
                    headers=header
                ).text
                print(result)
                logger.info(result)
                print(f'Message sent to {user.username}.')
                logger.info(f'Message sent to {user.username}.')

            except Exception as err:
                print(err)
                logger.error(err)


    def scan_list(self):
        # If stop_browser is set, then the chrome is started and closed each time
        if self.stop_browser:
            self.__init__browser()

        for web in self.web_list:
            # Set max time of failure
            failure_cnt = 2

            while failure_cnt > 0:
                try:
                    self.__scan(web)
                    break
                except Exception as err:
                    print(err)
                    logging.error(err)
                    failure_cnt -= 1
                    self.__close_browser()
                    self.__init__browser()

        # Close browser if stop_browser is set
        if self.stop_browser:
            self.__close_browser()

if __name__ == '__main__':
    # Start driver
    driver = ChromeDriver(web_list=[], token_list=[], stop_browser=True)

    while True:
        # Refresh web_list and token_list
        try:
            web_list_json = json.load(open('web_list.json', 'rt'))
            new_web_list: List[WebModel] = []
            for web in web_list_json:
                for exist_web in driver.web_list:
                    if exist_web.url == web['url'] and exist_web.xpath == web['xpath']:
                        new_web_list.append(exist_web)
                        break
                else:
                    new_web_list.append(WebModel(
                        title=web['title'],
                        url=web['url'],
                        xpath=web['xpath']
                    ))
                    logger.info(f'Added web {web["title"]}')
            driver.web_list = new_web_list
        except Exception as err:
            print(err)
            logger.error(err)

        try:
            token_list_json = json.load(open('token.json', 'rt'))
            new_token_list: List[UserModel] = []
            for token in token_list_json:
                for exist_token in driver.token_list:
                    if exist_token.token == token['token']:
                        new_token_list.append(exist_token)
                        break
                else:
                    new_token_list.append(UserModel(
                        username=token['username'],
                        token=token['token']
                    ))
                    logger.info(f'Added user {token["username"]}')
            driver.token_list = new_token_list
        except Exception as err:
            print(err)
            logger.error(err)

        # Start scan
        driver.scan_list()
        time.sleep(30)
